//servidor nivel 4
#include<stdlib.h>
#include<stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <time.h>

#define TAMBUFFER 4000
#define NUMTOT 1000
//portas que o servidor atende
#define PORT1 3234
#define PORT2 3235


typedef struct{
	int port;
	char* vetor;	
	double tempo; //tempo de transferencia de dados para servidor
}thread_arg;

void* recebeDadosCliente(void*);
double ordenaDados(char*, char*, int*);
void converteIntToChar(int*, char*);
void enviaDadosServidor(char*, int, char*);

int main(int argc, char **argv)
{
	char *vetorDados1, *vetorDados2, *dadosAEnviar;
	int *vetorOrdenado;
	int i;
	pthread_t pid1, pid2;
	thread_arg arg1, arg2;
	double tempoOrdenacao;
		
	if(argc < 3)
	{
		printf("Quantidade de argumentos errado: host, port\n");
		exit(1);
	}
	
	vetorDados1 = (char*) malloc(TAMBUFFER*sizeof(char));
	vetorDados2 = (char*) malloc(TAMBUFFER*sizeof(char));
	vetorOrdenado = (int*) malloc(NUMTOT*sizeof(int));
	dadosAEnviar = (char*) malloc((TAMBUFFER*2)*sizeof(char));
	
	arg1.port = PORT1;
	arg2.port = PORT2;
	arg1.vetor = vetorDados1;
	arg2.vetor = vetorDados2;
	arg1.tempo = 0.0;
	arg2.tempo = 0.0;
	
	pthread_create(&pid1, NULL, &recebeDadosCliente, &arg1); //threads para atender 2 clientes
	pthread_create(&pid2, NULL, &recebeDadosCliente, &arg2);
	pthread_join(pid1, NULL);
	pthread_join(pid2, NULL);
	
	printf("Cliente 1:\n%s\n", vetorDados1); //imprime conteudo dos vetores
	printf("Tempo de transferencia: %.3lf\n", arg1.tempo);
	printf("Cliente 2:\n%s\n", vetorDados2);
	printf("Tempo de tranferencia: %.3lf\n", arg2.tempo);
	
	//ordena os dados------------------------------------------------
	tempoOrdenacao = ordenaDados(vetorDados1, vetorDados2, vetorOrdenado);
	printf("\nVetor ordenado:\n");
	for(i=0; i< NUMTOT; i++)
	{
		printf("%d ", vetorOrdenado[i]);
	}
	printf("\nTempo de ordenacao: %.3lf\n", tempoOrdenacao);
	
	//envia dados para servidor pai-----------------------------------
	converteIntToChar(vetorOrdenado, dadosAEnviar);
	for(i=1; i < 9 ; i++)
	{
		enviaDadosServidor(argv[i],atoi(argv[9]),dadosAEnviar);
		printf("Enviou para %s\n", argv[i]);
	}
	
	return 0;
}

void* recebeDadosCliente(void* args) //funcao que pega dados  dos 2 clientes
{
	thread_arg* argumentos = (thread_arg*) args;
	char* vetorDados = (char*) argumentos->vetor; //referencia para vetor na main
	
	int sockfd, newsockfd, portno;
	socklen_t clilen;
	char *buffer;
	struct sockaddr_in serv_addr, cli_addr;
	int n;
	clock_t startTime;
	double tempo;
	
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		printf("Erro na criacao do socket\n");
	}
	
	bzero((char*) &serv_addr, sizeof(serv_addr));
	portno = argumentos->port;
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = INADDR_ANY;
	serv_addr.sin_port = htons(portno);
	
	if(bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0)
	{
		printf("Erro no bind");
	}	
	
	listen(sockfd, 5);
	clilen = sizeof(cli_addr);
	newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
	if(newsockfd < 0)
	{
		printf("Erro ao aceitar\n");
	}
	else
	{
		printf("Aceitou conexao\n");
	}
	
	buffer = (char*) malloc(TAMBUFFER*sizeof(char));
	startTime = clock(); //--------------------------inicio tempo de tranferencia
	n = read(newsockfd, buffer, TAMBUFFER);
	tempo = (double)(clock()-startTime)/CLOCKS_PER_SEC; //fim tempo de tranferencia
	argumentos->tempo = tempo;
	
	if(n < 0)
	{
		printf("Erro ao ler socket\n");
	}
	
	strcpy(vetorDados, buffer);
	close(sockfd);
	close(newsockfd);
	free(buffer);
	
}

double ordenaDados(char* vetChar1, char* vetChar2, int* vetOrdenado) //dados dos clientes aqui sao ordenados
{
	int i=0,j=0,k=0;
	int *vetInt1 = (int*)malloc((NUMTOT/2)*sizeof(int));
	int *vetInt2 = (int*)malloc((NUMTOT/2)*sizeof(int));
	char* str1 = strtok(vetChar1, " ");
	clock_t startTime;
	double tempo;
	
	while(str1 != NULL)
	{
		vetInt1[i] = atoi(str1);
		str1 = strtok(NULL, " ");
		i++;
	}
	
	str1 = strtok(vetChar2, " ");
	i=0;	
	while(str1 != NULL)
	{
		vetInt2[i] = atoi(str1);
		str1 = strtok(NULL, " ");
		i++;
	}
	
	startTime = clock(); //inicio tempo de ordenacao
	i=0;
	j=0;
	for(k=0; k<NUMTOT; k++)
	{
		if(i == (NUMTOT/2))
		{
			vetOrdenado[k] = vetInt2[j++];
			continue;
		}
		if(j == (NUMTOT/2))
		{
			vetOrdenado[k] = vetInt1[i++];
			continue;
		}
		if(vetInt1[i] < vetInt2[j])
		{
			vetOrdenado[k] = vetInt1[i++];
		}
		else
		{
			vetOrdenado[k] = vetInt2[j++];
		}
	}
	tempo = (double)(clock()-startTime)/CLOCKS_PER_SEC;
	free(vetInt1);
	free(vetInt2);
	return tempo;
}

void converteIntToChar(int* vetorInt, char* vetorChar) //converte vetor de int para vetor de char para enviar para servidor pai
{
	int i;
	char *buffer = (char*) malloc((TAMBUFFER*2)*sizeof(char));
	char str[15];
	
	strcpy(buffer, "\0");
	strcpy(str, "\0");
	for(i=0; i< NUMTOT; i++)
	{
		sprintf(str, "%d ", vetorInt[i]);
		strcat(buffer, str);
		strcpy(str, "\0");
	}
	strcat(buffer, "\0");
	strcpy(vetorChar, buffer);
	free(buffer);
}

void enviaDadosServidor(char* host, int port, char* dados) //envia dados para servidor pai
{
	int sockfd, portno = port, n;
	struct sockaddr_in serv_addr;
	struct hostent *server;
	
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		printf("Erro no socket\n");
	}
	
	server = gethostbyname(host);
	if(server == NULL)
	{
		printf("Erros no host\n");
	}
	
	bzero((char*) &serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	bcopy((char*) server->h_addr, (char*)&serv_addr.sin_addr.s_addr, server->h_length);
	serv_addr.sin_port = htons(portno);
	
	if(connect(sockfd,(struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
	{
		printf("\nErro na conexao\n");
		
	}
	
	n = write(sockfd, dados, strlen(dados));	
	if(n < 0)
	{
		printf("Erro de escrita em socket\n");
	}
	close(sockfd);
}

